package java7;


import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;

/**
 * @author wanyuxiang
 * @version 1.0
 * @project Java_base
 * @create 2019-10-24 15:49
 * @desc 动态编译java文件
 **/
public class CostomClassLoader extends ClassLoader {

    public static void main(String[] args) throws Exception {
        System.out.println(new CostomClassLoader().calculate("(3+2)*5/3"));
    }

    public double calculate(String exp) throws Exception {
        String className = "CalculatorMain";
        String methodName = "calculate";
        String source = "public class %s { public static double %s(){return %s;}}";
        source = String.format(source, className, methodName, exp);
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, Locale.CHINA, StandardCharsets.UTF_8);
        JavaFileObject sourceObject = new StringSourceJavaFileObject(className, source);
        Iterable<? extends JavaFileObject> fileObjects = Collections.singletonList(sourceObject);
        Path output = Files.createTempDirectory("calculator");
        Iterable<String> options = Arrays.asList("-d", output.toString());
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, fileObjects);
        boolean result = task.call();
        if (result) {
            byte[] classData = Files.readAllBytes(Paths.get(output.toString(), className + ".class"));
            Class<?> clazz = defineClass(className, classData, 0, classData.length);
            Method method = clazz.getMethod(methodName);
            Object value = method.invoke(null);//静态方法可省略对象，直接用null替代，或用clas
            return (Double) value;
        } else {
            throw new Exception("无法识别的表达式");
        }
    }

    static class StringSourceJavaFileObject extends SimpleJavaFileObject {
        private String content;

        public StringSourceJavaFileObject(String name, String content) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.content = content;
        }

        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return content;
        }
    }
}
